{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 使用 Unsloth 对 DeepSeek-R1-Distill-Qwen-1.5B 模型进行 LoRA 微调\n",
    "\n",
    "本 Notebook 展示了如何使用 `unsloth` 库对 `deepseek-ai/DeepSeek-R1-Distill-Qwen-1.5B` 模型进行高效的 QLoRA (Low-Rank Adaptation) 微调。\n",
    "\n",
    "整个流程包括：\n",
    "1.  环境准备与库导入\n",
    "2.  加载预训练模型和分词器 (Tokenizer)。\n",
    "3.  在微调前，对模型进行简单的推理测试。\n",
    "4.  下载和格式化训练数据集\n",
    "5.  使用 `unsloth` 的 `FastLanguageModel` 来为模型添加 LoRA 适配器。\n",
    "6.  配置 `SFTTrainer` 监督微调训练配置。\n",
    "7.  启动训练，并观察 Loss 变化情况\n",
    "8.  保存微调后的模型\n",
    "9.  测试训练后的生成结果"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 1. 环境准备与库导入\n",
    "\n",
    "首先，我们需要安装并导入所有必要的库。`transformers` 用于加载模型和分词器，`unsloth` 用于高效微调，`trl` 提供了 `SFTTrainer`，而 `datasets` 用于处理数据。\n",
    "\n",
    "**注意**: 在运行此 Notebook 之前，请确保已安装所有依赖包：\n",
    "```bash\n",
    "pip install -r requirements.txt\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "🦥 Unsloth: Will patch your computer to enable 2x faster free finetuning.\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/home/ubuntu/miniconda3/envs/qwenLora/lib/python3.13/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n",
      "  from .autonotebook import tqdm as notebook_tqdm\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "🦥 Unsloth Zoo will now patch everything to make training faster!\n"
     ]
    }
   ],
   "source": [
    "import torch\n",
    "from unsloth import FastLanguageModel\n",
    "from transformers import AutoTokenizer, AutoModelForCausalLM, BitsAndBytesConfig, GenerationConfig, DataCollatorForSeq2Seq\n",
    "from datasets import Dataset"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2. 加载预训练模型和分词器 (Tokenizer)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "==((====))==  Unsloth 2025.8.5: Fast Qwen2 patching. Transformers: 4.55.2.\n",
      "   \\\\   /|    Tesla T4. Num GPUs = 1. Max memory: 14.581 GB. Platform: Linux.\n",
      "O^O/ \\_/ \\    Torch: 2.7.1+cu126. CUDA: 7.5. CUDA Toolkit: 12.6. Triton: 3.3.1\n",
      "\\        /    Bfloat16 = FALSE. FA [Xformers = 0.0.31.post1. FA2 = False]\n",
      " \"-____-\"     Free license: http://github.com/unslothai/unsloth\n",
      "Unsloth: Fast downloading is enabled - ignore downloading bars which are red colored!\n"
     ]
    }
   ],
   "source": [
    "# 定义模型和一些基本参数\n",
    "max_seq_length = 8192\n",
    "dtype = None # None 表示自动选择 (Float16 a T4, V100, BFloat16 a Ampere)\n",
    "load_in_4bit = True # 使用 4bit 量化加载\n",
    "\n",
    "# 这是您的模型标识符，请替换为您正在使用的模型\n",
    "# 例如：\"qwen-1.5b_lora_model\"\n",
    "# model_name = \"qwen-1.5b_lora_model\" \n",
    "# model_name = \"unsloth/DeepSeek-R1-Distill-Qwen-1.5B\" \n",
    "model_name = \"unsloth/DeepSeek-R1-Distill-Qwen-1.5B-unsloth-bnb-4bit\" \n",
    "\n",
    "# 这一步会返回一个经过 Unsloth 优化的模型和一个分词器\n",
    "model, tokenizer = FastLanguageModel.from_pretrained(\n",
    "    model_name = model_name,\n",
    "    max_seq_length = max_seq_length,\n",
    "    dtype = dtype,\n",
    "    load_in_4bit = load_in_4bit,\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3. 微调前推理测试\n",
    "\n",
    "在对模型进行任何修改之前，我们先用它来生成一段文本，看看原始模型的表现如何。这可以作为我们微调效果的基准参考。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 模型推理的 Prompt 模板\n",
    "inference_prompt = \"\"\"以下是一条描述任务的指令，并配有一个提供进一步上下文的输入。\n",
    "请撰写一份恰当的回复，以完成该请求。\n",
    "在回答之前，请仔细思考该问题，并构建一个分步的思考过程，以确保回应的逻辑严谨和内容准确。\n",
    "\n",
    "\n",
    "### Instruction:\n",
    "你是一位医学专家，在临床推理、诊断学和治疗规划方面拥有深厚的专业知识。\n",
    "请回答以下医学问题。\n",
    "\n",
    "### Question:\n",
    "{}\n",
    "\n",
    "### Response:\n",
    "<think>{}\n",
    "\"\"\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "FastLanguageModel.for_inference(model)\n",
    "\n",
    "question = \"男，28岁，程序员，最近一周每天工作到半夜，感觉头晕、脖子疼，有时候还恶心。\"\n",
    "\n",
    "inputs = tokenizer([inference_prompt.format(question, \"\")], return_tensors=\"pt\").to(\"cuda\")\n",
    "attention_mask = inputs.input_ids.ne(tokenizer.pad_token_id).long().to(\"cuda\")\n",
    "\n",
    "outputs = model.generate(\n",
    "    input_ids=inputs.input_ids,\n",
    "    attention_mask=inputs.attention_mask,\n",
    "    max_new_tokens=1200,\n",
    "    use_cache=True,\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "response = tokenizer.batch_decode(outputs, skip_special_tokens=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "<think>\n",
      "好，我现在需要分析这位28岁的男性，他是一位程序员，最近每天工作到半夜，感觉头晕、脖子疼，还有时候恶心。首先，头晕和脖子疼可能与脑力劳动有关，但长时间工作可能导致神经损伤，特别是脑力劳动可能加重。恶心和头晕可能与脑力活动有关，也可能与焦虑或压力有关。\n",
      "\n",
      "考虑到他工作时间长，可能有持续的脑力劳动，这可能导致脑力损伤，尤其是长期的脑力劳动可能影响视力和神经功能。如果出现头晕、脖子疼，可能需要考虑是否是脑力损伤导致的，或者是否存在其他潜在问题。\n",
      "\n",
      "接下来，我会考虑是否有其他可能的解释，比如焦虑症、压力或长期的脑力劳动。如果有焦虑或压力，可能会导致头晕、恶心和脖子疼痛。如果是长期的脑力劳动，可能需要考虑是否需要进行适当的休息或营养补充，或者是否需要进一步的评估，比如神经科检查，以排除潜在的脑损伤。\n",
      "\n",
      "此外，也有可能是脑部损伤，比如脑膜炎或脑膜炎引起的脑水肿，这可能需要专业的诊断和治疗。因此，我需要建议进一步的评估，以确定是否存在脑部损伤，如果是的话，需要进行神经科检查，评估脑部功能，可能需要药物治疗，如神经保护药物或药物治疗，这可能对他的日常生活和工作产生影响。\n",
      "\n",
      "总的来说，这位患者可能有长期的脑力劳动引起的脑损伤，需要进一步的评估和治疗，以确保他的健康和安全。\n",
      "</think>\n",
      "\n",
      "这位28岁的男性是一位程序员，最近每天工作到半夜，表现出头晕、脖子疼、恶心等症状。考虑到长时间的工作和脑力劳动，可能与脑力损伤有关。建议进一步评估，包括神经科检查，以确定是否存在脑部损伤，如脑膜炎、脑膜炎引起的脑水肿或其他潜在问题。如果诊断出有脑损伤，可能需要药物治疗，这可能会影响他的日常生活。建议咨询神经科专家，以便获得更准确的诊断和治疗建议。\n"
     ]
    }
   ],
   "source": [
    "print(response[0].split(\"### Response:\")[1])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "\n",
    "### 4. 下载和格式化训练数据集\n",
    "\n",
    "\n",
    "医学推理数据集：https://huggingface.co/datasets/FreedomIntelligence/medical-o1-reasoning-SFT/viewer/zh\n",
    "\n",
    "![dataset](images/dataset.png)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 模型训练的 Prompt 模板\n",
    "train_prompt = \"\"\"以下是一条描述任务的指令，并配有一个提供进一步上下文的输入。\n",
    "请撰写一份恰当的回复，以完成该请求。\n",
    "在回答之前，请仔细思考该问题，并构建一个分步的思考过程，以确保回应的逻辑严谨和内容准确。\n",
    "\n",
    "\n",
    "### Instruction:\n",
    "你是一位医学专家，在临床推理、诊断学和治疗规划方面拥有深厚的专业知识。\n",
    "请回答以下医学问题。\n",
    "\n",
    "### Question:\n",
    "{}\n",
    "\n",
    "### Response:\n",
    "<think>\n",
    "{}\n",
    "</think>\n",
    "{}\n",
    "\"\"\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "EOS_TOKEN = tokenizer.eos_token # 添加 EOS Token\n",
    "\n",
    "def formatting_prompts_func(examples):\n",
    "    inputs = examples[\"Question\"]\n",
    "    cots = examples[\"Complex_CoT\"]\n",
    "    outputs = examples[\"Response\"]\n",
    "    texts = []\n",
    "    for input, cot, output in zip(inputs, cots, outputs):\n",
    "        # 将 EOS Token 添加到样本最后\n",
    "        text = train_prompt.format(input, cot, output) + EOS_TOKEN\n",
    "        texts.append(text)\n",
    "    return { \"text\" : texts, }\n",
    "pass\n",
    "\n",
    "from datasets import load_dataset\n",
    "dataset = load_dataset(\"FreedomIntelligence/medical-o1-reasoning-SFT\", \"zh\", split = \"train\")\n",
    "dataset = dataset.map(formatting_prompts_func, batched = True,)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'以下是一条描述任务的指令，并配有一个提供进一步上下文的输入。\\n请撰写一份恰当的回复，以完成该请求。\\n在回答之前，请仔细思考该问题，并构建一个分步的思考过程，以确保回应的逻辑严谨和内容准确。\\n\\n\\n### Instruction:\\n你是一位医学专家，在临床推理、诊断学和治疗规划方面拥有深厚的专业知识。\\n请回答以下医学问题。\\n\\n### Question:\\n根据描述，一个1岁的孩子在夏季头皮出现多处小结节，长期不愈合，且现在疮大如梅，溃破流脓，口不收敛，头皮下有空洞，患处皮肤增厚。这种病症在中医中诊断为什么病？\\n\\n### Response:\\n<think>\\n这个小孩子在夏天头皮上长了些小结节，一直都没好，后来变成了脓包，流了好多脓。想想夏天那么热，可能和湿热有关。才一岁的小孩，免疫力本来就不强，夏天的湿热没准就侵袭了身体。\\n\\n用中医的角度来看，出现小结节、再加上长期不愈合，这些症状让我想到了头疮。小孩子最容易得这些皮肤病，主要因为湿热在体表郁结。\\n\\n但再看看，头皮下还有空洞，这可能不止是简单的头疮。看起来病情挺严重的，也许是脓肿没治好。这样的情况中医中有时候叫做禿疮或者湿疮，也可能是另一种情况。\\n\\n等一下，头皮上的空洞和皮肤增厚更像是疾病已经深入到头皮下，这是不是说明有可能是流注或瘰疬？这些名字常描述头部或颈部的严重感染，特别是有化脓不愈合，又形成通道或空洞的情况。\\n\\n仔细想想，我怎么感觉这些症状更贴近瘰疬的表现？尤其考虑到孩子的年纪和夏天发生的季节性因素，湿热可能是主因，但可能也有火毒或者痰湿造成的滞留。\\n\\n回到基本的症状描述上看，这种长期不愈合又复杂的状况，如果结合中医更偏重的病名，是不是有可能是涉及更深层次的感染？\\n\\n再考虑一下，这应该不是单纯的瘰疬，得仔细分析头皮增厚并出现空洞这样的严重症状。中医里头，这样的表现可能更符合‘蚀疮’或‘头疽’。这些病名通常描述头部严重感染后的溃烂和组织坏死。\\n\\n看看季节和孩子的体质，夏天又湿又热，外邪很容易侵入头部，对孩子这么弱的免疫系统简直就是挑战。头疽这个病名听起来真是切合，因为它描述的感染严重，溃烂到出现空洞。\\n\\n不过，仔细琢磨后发现，还有个病名似乎更为合适，叫做‘蝼蛄疖’，这病在中医里专指像这种严重感染并伴有深部空洞的情况。它也涵盖了化脓和皮肤增厚这些症状。\\n\\n哦，该不会是夏季湿热，导致湿毒入侵，孩子的体质不能御，其病情发展成这样的感染？综合分析后我觉得‘蝼蛄疖’这个病名真是相当符合。\\n</think>\\n从中医的角度来看，你所描述的症状符合“蝼蛄疖”的病症。这种病症通常发生在头皮，表现为多处结节，溃破流脓，形成空洞，患处皮肤增厚且长期不愈合。湿热较重的夏季更容易导致这种病症的发展，特别是在免疫力较弱的儿童身上。建议结合中医的清热解毒、祛湿消肿的治疗方法进行处理，并配合专业的医疗建议进行详细诊断和治疗。\\n<｜end▁of▁sentence｜>'"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "dataset[0][\"text\"]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/markdown": [
       "以下是一条描述任务的指令，并配有一个提供进一步上下文的输入。\n",
       "请撰写一份恰当的回复，以完成该请求。\n",
       "在回答之前，请仔细思考该问题，并构建一个分步的思考过程，以确保回应的逻辑严谨和内容准确。\n",
       "\n",
       "\n",
       "### Instruction:\n",
       "你是一位医学专家，在临床推理、诊断学和治疗规划方面拥有深厚的专业知识。\n",
       "请回答以下医学问题。\n",
       "\n",
       "### Question:\n",
       "根据描述，一个1岁的孩子在夏季头皮出现多处小结节，长期不愈合，且现在疮大如梅，溃破流脓，口不收敛，头皮下有空洞，患处皮肤增厚。这种病症在中医中诊断为什么病？\n",
       "\n",
       "### Response:\n",
       "<think>\n",
       "这个小孩子在夏天头皮上长了些小结节，一直都没好，后来变成了脓包，流了好多脓。想想夏天那么热，可能和湿热有关。才一岁的小孩，免疫力本来就不强，夏天的湿热没准就侵袭了身体。\n",
       "\n",
       "用中医的角度来看，出现小结节、再加上长期不愈合，这些症状让我想到了头疮。小孩子最容易得这些皮肤病，主要因为湿热在体表郁结。\n",
       "\n",
       "但再看看，头皮下还有空洞，这可能不止是简单的头疮。看起来病情挺严重的，也许是脓肿没治好。这样的情况中医中有时候叫做禿疮或者湿疮，也可能是另一种情况。\n",
       "\n",
       "等一下，头皮上的空洞和皮肤增厚更像是疾病已经深入到头皮下，这是不是说明有可能是流注或瘰疬？这些名字常描述头部或颈部的严重感染，特别是有化脓不愈合，又形成通道或空洞的情况。\n",
       "\n",
       "仔细想想，我怎么感觉这些症状更贴近瘰疬的表现？尤其考虑到孩子的年纪和夏天发生的季节性因素，湿热可能是主因，但可能也有火毒或者痰湿造成的滞留。\n",
       "\n",
       "回到基本的症状描述上看，这种长期不愈合又复杂的状况，如果结合中医更偏重的病名，是不是有可能是涉及更深层次的感染？\n",
       "\n",
       "再考虑一下，这应该不是单纯的瘰疬，得仔细分析头皮增厚并出现空洞这样的严重症状。中医里头，这样的表现可能更符合‘蚀疮’或‘头疽’。这些病名通常描述头部严重感染后的溃烂和组织坏死。\n",
       "\n",
       "看看季节和孩子的体质，夏天又湿又热，外邪很容易侵入头部，对孩子这么弱的免疫系统简直就是挑战。头疽这个病名听起来真是切合，因为它描述的感染严重，溃烂到出现空洞。\n",
       "\n",
       "不过，仔细琢磨后发现，还有个病名似乎更为合适，叫做‘蝼蛄疖’，这病在中医里专指像这种严重感染并伴有深部空洞的情况。它也涵盖了化脓和皮肤增厚这些症状。\n",
       "\n",
       "哦，该不会是夏季湿热，导致湿毒入侵，孩子的体质不能御，其病情发展成这样的感染？综合分析后我觉得‘蝼蛄疖’这个病名真是相当符合。\n",
       "</think>\n",
       "从中医的角度来看，你所描述的症状符合“蝼蛄疖”的病症。这种病症通常发生在头皮，表现为多处结节，溃破流脓，形成空洞，患处皮肤增厚且长期不愈合。湿热较重的夏季更容易导致这种病症的发展，特别是在免疫力较弱的儿童身上。建议结合中医的清热解毒、祛湿消肿的治疗方法进行处理，并配合专业的医疗建议进行详细诊断和治疗。\n",
       "<｜end▁of▁sentence｜>"
      ],
      "text/plain": [
       "<IPython.core.display.Markdown object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "from IPython.display import display, Markdown\n",
    "\n",
    "display(Markdown(dataset[0][\"text\"]))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 5. 使用 Unsloth 添加 LoRA 适配器\n",
    "\n",
    "这是使用 `unsloth` 的核心步骤。我们调用 `FastLanguageModel.get_peft_model`，它会非常高效地为模型注入 LoRA 模块。\n",
    "\n",
    "- `r`: LoRA 的秩 (rank)，是控制模型复杂度和参数量的关键超参数。\n",
    "- `target_modules`: 指定要在哪些线性层（如注意力机制的 q, k, v, o 投影层）上应用 LoRA。\n",
    "- `lora_alpha`: LoRA 的缩放因子，通常设置为 `r` 的两倍或与 `r` 相同。\n",
    "- `use_gradient_checkpointing`: 一种节省显存的技术，对于训练大模型至关重要。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Unsloth 2025.8.5 patched 28 layers with 28 QKV layers, 28 O layers and 28 MLP layers.\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "PeftModelForCausalLM(\n",
      "  (base_model): LoraModel(\n",
      "    (model): Qwen2ForCausalLM(\n",
      "      (model): Qwen2Model(\n",
      "        (embed_tokens): Embedding(151936, 1536, padding_idx=151654)\n",
      "        (layers): ModuleList(\n",
      "          (0): Qwen2DecoderLayer(\n",
      "            (self_attn): Qwen2Attention(\n",
      "              (q_proj): lora.Linear(\n",
      "                (base_layer): Linear(in_features=1536, out_features=1536, bias=True)\n",
      "                (lora_dropout): ModuleDict(\n",
      "                  (default): Identity()\n",
      "                )\n",
      "                (lora_A): ModuleDict(\n",
      "                  (default): Linear(in_features=1536, out_features=16, bias=False)\n",
      "                )\n",
      "                (lora_B): ModuleDict(\n",
      "                  (default): Linear(in_features=16, out_features=1536, bias=False)\n",
      "                )\n",
      "                (lora_embedding_A): ParameterDict()\n",
      "                (lora_embedding_B): ParameterDict()\n",
      "                (lora_magnitude_vector): ModuleDict()\n",
      "              )\n",
      "              (k_proj): lora.Linear(\n",
      "                (base_layer): Linear(in_features=1536, out_features=256, bias=True)\n",
      "                (lora_dropout): ModuleDict(\n",
      "                  (default): Identity()\n",
      "                )\n",
      "                (lora_A): ModuleDict(\n",
      "                  (default): Linear(in_features=1536, out_features=16, bias=False)\n",
      "                )\n",
      "                (lora_B): ModuleDict(\n",
      "                  (default): Linear(in_features=16, out_features=256, bias=False)\n",
      "                )\n",
      "                (lora_embedding_A): ParameterDict()\n",
      "                (lora_embedding_B): ParameterDict()\n",
      "                (lora_magnitude_vector): ModuleDict()\n",
      "              )\n",
      "              (v_proj): lora.Linear(\n",
      "                (base_layer): Linear(in_features=1536, out_features=256, bias=True)\n",
      "                (lora_dropout): ModuleDict(\n",
      "                  (default): Identity()\n",
      "                )\n",
      "                (lora_A): ModuleDict(\n",
      "                  (default): Linear(in_features=1536, out_features=16, bias=False)\n",
      "                )\n",
      "                (lora_B): ModuleDict(\n",
      "                  (default): Linear(in_features=16, out_features=256, bias=False)\n",
      "                )\n",
      "                (lora_embedding_A): ParameterDict()\n",
      "                (lora_embedding_B): ParameterDict()\n",
      "                (lora_magnitude_vector): ModuleDict()\n",
      "              )\n",
      "              (o_proj): lora.Linear(\n",
      "                (base_layer): Linear(in_features=1536, out_features=1536, bias=False)\n",
      "                (lora_dropout): ModuleDict(\n",
      "                  (default): Identity()\n",
      "                )\n",
      "                (lora_A): ModuleDict(\n",
      "                  (default): Linear(in_features=1536, out_features=16, bias=False)\n",
      "                )\n",
      "                (lora_B): ModuleDict(\n",
      "                  (default): Linear(in_features=16, out_features=1536, bias=False)\n",
      "                )\n",
      "                (lora_embedding_A): ParameterDict()\n",
      "                (lora_embedding_B): ParameterDict()\n",
      "                (lora_magnitude_vector): ModuleDict()\n",
      "              )\n",
      "              (rotary_emb): LlamaRotaryEmbedding()\n",
      "            )\n",
      "            (mlp): Qwen2MLP(\n",
      "              (gate_proj): lora.Linear4bit(\n",
      "                (base_layer): Linear4bit(in_features=1536, out_features=8960, bias=False)\n",
      "                (lora_dropout): ModuleDict(\n",
      "                  (default): Identity()\n",
      "                )\n",
      "                (lora_A): ModuleDict(\n",
      "                  (default): Linear(in_features=1536, out_features=16, bias=False)\n",
      "                )\n",
      "                (lora_B): ModuleDict(\n",
      "                  (default): Linear(in_features=16, out_features=8960, bias=False)\n",
      "                )\n",
      "                (lora_embedding_A): ParameterDict()\n",
      "                (lora_embedding_B): ParameterDict()\n",
      "                (lora_magnitude_vector): ModuleDict()\n",
      "              )\n",
      "              (up_proj): lora.Linear4bit(\n",
      "                (base_layer): Linear4bit(in_features=1536, out_features=8960, bias=False)\n",
      "                (lora_dropout): ModuleDict(\n",
      "                  (default): Identity()\n",
      "                )\n",
      "                (lora_A): ModuleDict(\n",
      "                  (default): Linear(in_features=1536, out_features=16, bias=False)\n",
      "                )\n",
      "                (lora_B): ModuleDict(\n",
      "                  (default): Linear(in_features=16, out_features=8960, bias=False)\n",
      "                )\n",
      "                (lora_embedding_A): ParameterDict()\n",
      "                (lora_embedding_B): ParameterDict()\n",
      "                (lora_magnitude_vector): ModuleDict()\n",
      "              )\n",
      "              (down_proj): lora.Linear4bit(\n",
      "                (base_layer): Linear4bit(in_features=8960, out_features=1536, bias=False)\n",
      "                (lora_dropout): ModuleDict(\n",
      "                  (default): Identity()\n",
      "                )\n",
      "                (lora_A): ModuleDict(\n",
      "                  (default): Linear(in_features=8960, out_features=16, bias=False)\n",
      "                )\n",
      "                (lora_B): ModuleDict(\n",
      "                  (default): Linear(in_features=16, out_features=1536, bias=False)\n",
      "                )\n",
      "                (lora_embedding_A): ParameterDict()\n",
      "                (lora_embedding_B): ParameterDict()\n",
      "                (lora_magnitude_vector): ModuleDict()\n",
      "              )\n",
      "              (act_fn): SiLU()\n",
      "            )\n",
      "            (input_layernorm): Qwen2RMSNorm((1536,), eps=1e-06)\n",
      "            (post_attention_layernorm): Qwen2RMSNorm((1536,), eps=1e-06)\n",
      "          )\n",
      "          (1-2): 2 x Qwen2DecoderLayer(\n",
      "            (self_attn): Qwen2Attention(\n",
      "              (q_proj): lora.Linear4bit(\n",
      "                (base_layer): Linear4bit(in_features=1536, out_features=1536, bias=True)\n",
      "                (lora_dropout): ModuleDict(\n",
      "                  (default): Identity()\n",
      "                )\n",
      "                (lora_A): ModuleDict(\n",
      "                  (default): Linear(in_features=1536, out_features=16, bias=False)\n",
      "                )\n",
      "                (lora_B): ModuleDict(\n",
      "                  (default): Linear(in_features=16, out_features=1536, bias=False)\n",
      "                )\n",
      "                (lora_embedding_A): ParameterDict()\n",
      "                (lora_embedding_B): ParameterDict()\n",
      "                (lora_magnitude_vector): ModuleDict()\n",
      "              )\n",
      "              (k_proj): lora.Linear4bit(\n",
      "                (base_layer): Linear4bit(in_features=1536, out_features=256, bias=True)\n",
      "                (lora_dropout): ModuleDict(\n",
      "                  (default): Identity()\n",
      "                )\n",
      "                (lora_A): ModuleDict(\n",
      "                  (default): Linear(in_features=1536, out_features=16, bias=False)\n",
      "                )\n",
      "                (lora_B): ModuleDict(\n",
      "                  (default): Linear(in_features=16, out_features=256, bias=False)\n",
      "                )\n",
      "                (lora_embedding_A): ParameterDict()\n",
      "                (lora_embedding_B): ParameterDict()\n",
      "                (lora_magnitude_vector): ModuleDict()\n",
      "              )\n",
      "              (v_proj): lora.Linear4bit(\n",
      "                (base_layer): Linear4bit(in_features=1536, out_features=256, bias=True)\n",
      "                (lora_dropout): ModuleDict(\n",
      "                  (default): Identity()\n",
      "                )\n",
      "                (lora_A): ModuleDict(\n",
      "                  (default): Linear(in_features=1536, out_features=16, bias=False)\n",
      "                )\n",
      "                (lora_B): ModuleDict(\n",
      "                  (default): Linear(in_features=16, out_features=256, bias=False)\n",
      "                )\n",
      "                (lora_embedding_A): ParameterDict()\n",
      "                (lora_embedding_B): ParameterDict()\n",
      "                (lora_magnitude_vector): ModuleDict()\n",
      "              )\n",
      "              (o_proj): lora.Linear4bit(\n",
      "                (base_layer): Linear4bit(in_features=1536, out_features=1536, bias=False)\n",
      "                (lora_dropout): ModuleDict(\n",
      "                  (default): Identity()\n",
      "                )\n",
      "                (lora_A): ModuleDict(\n",
      "                  (default): Linear(in_features=1536, out_features=16, bias=False)\n",
      "                )\n",
      "                (lora_B): ModuleDict(\n",
      "                  (default): Linear(in_features=16, out_features=1536, bias=False)\n",
      "                )\n",
      "                (lora_embedding_A): ParameterDict()\n",
      "                (lora_embedding_B): ParameterDict()\n",
      "                (lora_magnitude_vector): ModuleDict()\n",
      "              )\n",
      "              (rotary_emb): LlamaRotaryEmbedding()\n",
      "            )\n",
      "            (mlp): Qwen2MLP(\n",
      "              (gate_proj): lora.Linear(\n",
      "                (base_layer): Linear(in_features=1536, out_features=8960, bias=False)\n",
      "                (lora_dropout): ModuleDict(\n",
      "                  (default): Identity()\n",
      "                )\n",
      "                (lora_A): ModuleDict(\n",
      "                  (default): Linear(in_features=1536, out_features=16, bias=False)\n",
      "                )\n",
      "                (lora_B): ModuleDict(\n",
      "                  (default): Linear(in_features=16, out_features=8960, bias=False)\n",
      "                )\n",
      "                (lora_embedding_A): ParameterDict()\n",
      "                (lora_embedding_B): ParameterDict()\n",
      "                (lora_magnitude_vector): ModuleDict()\n",
      "              )\n",
      "              (up_proj): lora.Linear(\n",
      "                (base_layer): Linear(in_features=1536, out_features=8960, bias=False)\n",
      "                (lora_dropout): ModuleDict(\n",
      "                  (default): Identity()\n",
      "                )\n",
      "                (lora_A): ModuleDict(\n",
      "                  (default): Linear(in_features=1536, out_features=16, bias=False)\n",
      "                )\n",
      "                (lora_B): ModuleDict(\n",
      "                  (default): Linear(in_features=16, out_features=8960, bias=False)\n",
      "                )\n",
      "                (lora_embedding_A): ParameterDict()\n",
      "                (lora_embedding_B): ParameterDict()\n",
      "                (lora_magnitude_vector): ModuleDict()\n",
      "              )\n",
      "              (down_proj): lora.Linear(\n",
      "                (base_layer): Linear(in_features=8960, out_features=1536, bias=False)\n",
      "                (lora_dropout): ModuleDict(\n",
      "                  (default): Identity()\n",
      "                )\n",
      "                (lora_A): ModuleDict(\n",
      "                  (default): Linear(in_features=8960, out_features=16, bias=False)\n",
      "                )\n",
      "                (lora_B): ModuleDict(\n",
      "                  (default): Linear(in_features=16, out_features=1536, bias=False)\n",
      "                )\n",
      "                (lora_embedding_A): ParameterDict()\n",
      "                (lora_embedding_B): ParameterDict()\n",
      "                (lora_magnitude_vector): ModuleDict()\n",
      "              )\n",
      "              (act_fn): SiLU()\n",
      "            )\n",
      "            (input_layernorm): Qwen2RMSNorm((1536,), eps=1e-06)\n",
      "            (post_attention_layernorm): Qwen2RMSNorm((1536,), eps=1e-06)\n",
      "          )\n",
      "          (3-25): 23 x Qwen2DecoderLayer(\n",
      "            (self_attn): Qwen2Attention(\n",
      "              (q_proj): lora.Linear4bit(\n",
      "                (base_layer): Linear4bit(in_features=1536, out_features=1536, bias=True)\n",
      "                (lora_dropout): ModuleDict(\n",
      "                  (default): Identity()\n",
      "                )\n",
      "                (lora_A): ModuleDict(\n",
      "                  (default): Linear(in_features=1536, out_features=16, bias=False)\n",
      "                )\n",
      "                (lora_B): ModuleDict(\n",
      "                  (default): Linear(in_features=16, out_features=1536, bias=False)\n",
      "                )\n",
      "                (lora_embedding_A): ParameterDict()\n",
      "                (lora_embedding_B): ParameterDict()\n",
      "                (lora_magnitude_vector): ModuleDict()\n",
      "              )\n",
      "              (k_proj): lora.Linear4bit(\n",
      "                (base_layer): Linear4bit(in_features=1536, out_features=256, bias=True)\n",
      "                (lora_dropout): ModuleDict(\n",
      "                  (default): Identity()\n",
      "                )\n",
      "                (lora_A): ModuleDict(\n",
      "                  (default): Linear(in_features=1536, out_features=16, bias=False)\n",
      "                )\n",
      "                (lora_B): ModuleDict(\n",
      "                  (default): Linear(in_features=16, out_features=256, bias=False)\n",
      "                )\n",
      "                (lora_embedding_A): ParameterDict()\n",
      "                (lora_embedding_B): ParameterDict()\n",
      "                (lora_magnitude_vector): ModuleDict()\n",
      "              )\n",
      "              (v_proj): lora.Linear4bit(\n",
      "                (base_layer): Linear4bit(in_features=1536, out_features=256, bias=True)\n",
      "                (lora_dropout): ModuleDict(\n",
      "                  (default): Identity()\n",
      "                )\n",
      "                (lora_A): ModuleDict(\n",
      "                  (default): Linear(in_features=1536, out_features=16, bias=False)\n",
      "                )\n",
      "                (lora_B): ModuleDict(\n",
      "                  (default): Linear(in_features=16, out_features=256, bias=False)\n",
      "                )\n",
      "                (lora_embedding_A): ParameterDict()\n",
      "                (lora_embedding_B): ParameterDict()\n",
      "                (lora_magnitude_vector): ModuleDict()\n",
      "              )\n",
      "              (o_proj): lora.Linear4bit(\n",
      "                (base_layer): Linear4bit(in_features=1536, out_features=1536, bias=False)\n",
      "                (lora_dropout): ModuleDict(\n",
      "                  (default): Identity()\n",
      "                )\n",
      "                (lora_A): ModuleDict(\n",
      "                  (default): Linear(in_features=1536, out_features=16, bias=False)\n",
      "                )\n",
      "                (lora_B): ModuleDict(\n",
      "                  (default): Linear(in_features=16, out_features=1536, bias=False)\n",
      "                )\n",
      "                (lora_embedding_A): ParameterDict()\n",
      "                (lora_embedding_B): ParameterDict()\n",
      "                (lora_magnitude_vector): ModuleDict()\n",
      "              )\n",
      "              (rotary_emb): LlamaRotaryEmbedding()\n",
      "            )\n",
      "            (mlp): Qwen2MLP(\n",
      "              (gate_proj): lora.Linear4bit(\n",
      "                (base_layer): Linear4bit(in_features=1536, out_features=8960, bias=False)\n",
      "                (lora_dropout): ModuleDict(\n",
      "                  (default): Identity()\n",
      "                )\n",
      "                (lora_A): ModuleDict(\n",
      "                  (default): Linear(in_features=1536, out_features=16, bias=False)\n",
      "                )\n",
      "                (lora_B): ModuleDict(\n",
      "                  (default): Linear(in_features=16, out_features=8960, bias=False)\n",
      "                )\n",
      "                (lora_embedding_A): ParameterDict()\n",
      "                (lora_embedding_B): ParameterDict()\n",
      "                (lora_magnitude_vector): ModuleDict()\n",
      "              )\n",
      "              (up_proj): lora.Linear4bit(\n",
      "                (base_layer): Linear4bit(in_features=1536, out_features=8960, bias=False)\n",
      "                (lora_dropout): ModuleDict(\n",
      "                  (default): Identity()\n",
      "                )\n",
      "                (lora_A): ModuleDict(\n",
      "                  (default): Linear(in_features=1536, out_features=16, bias=False)\n",
      "                )\n",
      "                (lora_B): ModuleDict(\n",
      "                  (default): Linear(in_features=16, out_features=8960, bias=False)\n",
      "                )\n",
      "                (lora_embedding_A): ParameterDict()\n",
      "                (lora_embedding_B): ParameterDict()\n",
      "                (lora_magnitude_vector): ModuleDict()\n",
      "              )\n",
      "              (down_proj): lora.Linear4bit(\n",
      "                (base_layer): Linear4bit(in_features=8960, out_features=1536, bias=False)\n",
      "                (lora_dropout): ModuleDict(\n",
      "                  (default): Identity()\n",
      "                )\n",
      "                (lora_A): ModuleDict(\n",
      "                  (default): Linear(in_features=8960, out_features=16, bias=False)\n",
      "                )\n",
      "                (lora_B): ModuleDict(\n",
      "                  (default): Linear(in_features=16, out_features=1536, bias=False)\n",
      "                )\n",
      "                (lora_embedding_A): ParameterDict()\n",
      "                (lora_embedding_B): ParameterDict()\n",
      "                (lora_magnitude_vector): ModuleDict()\n",
      "              )\n",
      "              (act_fn): SiLU()\n",
      "            )\n",
      "            (input_layernorm): Qwen2RMSNorm((1536,), eps=1e-06)\n",
      "            (post_attention_layernorm): Qwen2RMSNorm((1536,), eps=1e-06)\n",
      "          )\n",
      "          (26): Qwen2DecoderLayer(\n",
      "            (self_attn): Qwen2Attention(\n",
      "              (q_proj): lora.Linear4bit(\n",
      "                (base_layer): Linear4bit(in_features=1536, out_features=1536, bias=True)\n",
      "                (lora_dropout): ModuleDict(\n",
      "                  (default): Identity()\n",
      "                )\n",
      "                (lora_A): ModuleDict(\n",
      "                  (default): Linear(in_features=1536, out_features=16, bias=False)\n",
      "                )\n",
      "                (lora_B): ModuleDict(\n",
      "                  (default): Linear(in_features=16, out_features=1536, bias=False)\n",
      "                )\n",
      "                (lora_embedding_A): ParameterDict()\n",
      "                (lora_embedding_B): ParameterDict()\n",
      "                (lora_magnitude_vector): ModuleDict()\n",
      "              )\n",
      "              (k_proj): lora.Linear4bit(\n",
      "                (base_layer): Linear4bit(in_features=1536, out_features=256, bias=True)\n",
      "                (lora_dropout): ModuleDict(\n",
      "                  (default): Identity()\n",
      "                )\n",
      "                (lora_A): ModuleDict(\n",
      "                  (default): Linear(in_features=1536, out_features=16, bias=False)\n",
      "                )\n",
      "                (lora_B): ModuleDict(\n",
      "                  (default): Linear(in_features=16, out_features=256, bias=False)\n",
      "                )\n",
      "                (lora_embedding_A): ParameterDict()\n",
      "                (lora_embedding_B): ParameterDict()\n",
      "                (lora_magnitude_vector): ModuleDict()\n",
      "              )\n",
      "              (v_proj): lora.Linear4bit(\n",
      "                (base_layer): Linear4bit(in_features=1536, out_features=256, bias=True)\n",
      "                (lora_dropout): ModuleDict(\n",
      "                  (default): Identity()\n",
      "                )\n",
      "                (lora_A): ModuleDict(\n",
      "                  (default): Linear(in_features=1536, out_features=16, bias=False)\n",
      "                )\n",
      "                (lora_B): ModuleDict(\n",
      "                  (default): Linear(in_features=16, out_features=256, bias=False)\n",
      "                )\n",
      "                (lora_embedding_A): ParameterDict()\n",
      "                (lora_embedding_B): ParameterDict()\n",
      "                (lora_magnitude_vector): ModuleDict()\n",
      "              )\n",
      "              (o_proj): lora.Linear4bit(\n",
      "                (base_layer): Linear4bit(in_features=1536, out_features=1536, bias=False)\n",
      "                (lora_dropout): ModuleDict(\n",
      "                  (default): Identity()\n",
      "                )\n",
      "                (lora_A): ModuleDict(\n",
      "                  (default): Linear(in_features=1536, out_features=16, bias=False)\n",
      "                )\n",
      "                (lora_B): ModuleDict(\n",
      "                  (default): Linear(in_features=16, out_features=1536, bias=False)\n",
      "                )\n",
      "                (lora_embedding_A): ParameterDict()\n",
      "                (lora_embedding_B): ParameterDict()\n",
      "                (lora_magnitude_vector): ModuleDict()\n",
      "              )\n",
      "              (rotary_emb): LlamaRotaryEmbedding()\n",
      "            )\n",
      "            (mlp): Qwen2MLP(\n",
      "              (gate_proj): lora.Linear(\n",
      "                (base_layer): Linear(in_features=1536, out_features=8960, bias=False)\n",
      "                (lora_dropout): ModuleDict(\n",
      "                  (default): Identity()\n",
      "                )\n",
      "                (lora_A): ModuleDict(\n",
      "                  (default): Linear(in_features=1536, out_features=16, bias=False)\n",
      "                )\n",
      "                (lora_B): ModuleDict(\n",
      "                  (default): Linear(in_features=16, out_features=8960, bias=False)\n",
      "                )\n",
      "                (lora_embedding_A): ParameterDict()\n",
      "                (lora_embedding_B): ParameterDict()\n",
      "                (lora_magnitude_vector): ModuleDict()\n",
      "              )\n",
      "              (up_proj): lora.Linear(\n",
      "                (base_layer): Linear(in_features=1536, out_features=8960, bias=False)\n",
      "                (lora_dropout): ModuleDict(\n",
      "                  (default): Identity()\n",
      "                )\n",
      "                (lora_A): ModuleDict(\n",
      "                  (default): Linear(in_features=1536, out_features=16, bias=False)\n",
      "                )\n",
      "                (lora_B): ModuleDict(\n",
      "                  (default): Linear(in_features=16, out_features=8960, bias=False)\n",
      "                )\n",
      "                (lora_embedding_A): ParameterDict()\n",
      "                (lora_embedding_B): ParameterDict()\n",
      "                (lora_magnitude_vector): ModuleDict()\n",
      "              )\n",
      "              (down_proj): lora.Linear(\n",
      "                (base_layer): Linear(in_features=8960, out_features=1536, bias=False)\n",
      "                (lora_dropout): ModuleDict(\n",
      "                  (default): Identity()\n",
      "                )\n",
      "                (lora_A): ModuleDict(\n",
      "                  (default): Linear(in_features=8960, out_features=16, bias=False)\n",
      "                )\n",
      "                (lora_B): ModuleDict(\n",
      "                  (default): Linear(in_features=16, out_features=1536, bias=False)\n",
      "                )\n",
      "                (lora_embedding_A): ParameterDict()\n",
      "                (lora_embedding_B): ParameterDict()\n",
      "                (lora_magnitude_vector): ModuleDict()\n",
      "              )\n",
      "              (act_fn): SiLU()\n",
      "            )\n",
      "            (input_layernorm): Qwen2RMSNorm((1536,), eps=1e-06)\n",
      "            (post_attention_layernorm): Qwen2RMSNorm((1536,), eps=1e-06)\n",
      "          )\n",
      "          (27): Qwen2DecoderLayer(\n",
      "            (self_attn): Qwen2Attention(\n",
      "              (q_proj): lora.Linear4bit(\n",
      "                (base_layer): Linear4bit(in_features=1536, out_features=1536, bias=True)\n",
      "                (lora_dropout): ModuleDict(\n",
      "                  (default): Identity()\n",
      "                )\n",
      "                (lora_A): ModuleDict(\n",
      "                  (default): Linear(in_features=1536, out_features=16, bias=False)\n",
      "                )\n",
      "                (lora_B): ModuleDict(\n",
      "                  (default): Linear(in_features=16, out_features=1536, bias=False)\n",
      "                )\n",
      "                (lora_embedding_A): ParameterDict()\n",
      "                (lora_embedding_B): ParameterDict()\n",
      "                (lora_magnitude_vector): ModuleDict()\n",
      "              )\n",
      "              (k_proj): lora.Linear4bit(\n",
      "                (base_layer): Linear4bit(in_features=1536, out_features=256, bias=True)\n",
      "                (lora_dropout): ModuleDict(\n",
      "                  (default): Identity()\n",
      "                )\n",
      "                (lora_A): ModuleDict(\n",
      "                  (default): Linear(in_features=1536, out_features=16, bias=False)\n",
      "                )\n",
      "                (lora_B): ModuleDict(\n",
      "                  (default): Linear(in_features=16, out_features=256, bias=False)\n",
      "                )\n",
      "                (lora_embedding_A): ParameterDict()\n",
      "                (lora_embedding_B): ParameterDict()\n",
      "                (lora_magnitude_vector): ModuleDict()\n",
      "              )\n",
      "              (v_proj): lora.Linear4bit(\n",
      "                (base_layer): Linear4bit(in_features=1536, out_features=256, bias=True)\n",
      "                (lora_dropout): ModuleDict(\n",
      "                  (default): Identity()\n",
      "                )\n",
      "                (lora_A): ModuleDict(\n",
      "                  (default): Linear(in_features=1536, out_features=16, bias=False)\n",
      "                )\n",
      "                (lora_B): ModuleDict(\n",
      "                  (default): Linear(in_features=16, out_features=256, bias=False)\n",
      "                )\n",
      "                (lora_embedding_A): ParameterDict()\n",
      "                (lora_embedding_B): ParameterDict()\n",
      "                (lora_magnitude_vector): ModuleDict()\n",
      "              )\n",
      "              (o_proj): lora.Linear(\n",
      "                (base_layer): Linear(in_features=1536, out_features=1536, bias=False)\n",
      "                (lora_dropout): ModuleDict(\n",
      "                  (default): Identity()\n",
      "                )\n",
      "                (lora_A): ModuleDict(\n",
      "                  (default): Linear(in_features=1536, out_features=16, bias=False)\n",
      "                )\n",
      "                (lora_B): ModuleDict(\n",
      "                  (default): Linear(in_features=16, out_features=1536, bias=False)\n",
      "                )\n",
      "                (lora_embedding_A): ParameterDict()\n",
      "                (lora_embedding_B): ParameterDict()\n",
      "                (lora_magnitude_vector): ModuleDict()\n",
      "              )\n",
      "              (rotary_emb): LlamaRotaryEmbedding()\n",
      "            )\n",
      "            (mlp): Qwen2MLP(\n",
      "              (gate_proj): lora.Linear4bit(\n",
      "                (base_layer): Linear4bit(in_features=1536, out_features=8960, bias=False)\n",
      "                (lora_dropout): ModuleDict(\n",
      "                  (default): Identity()\n",
      "                )\n",
      "                (lora_A): ModuleDict(\n",
      "                  (default): Linear(in_features=1536, out_features=16, bias=False)\n",
      "                )\n",
      "                (lora_B): ModuleDict(\n",
      "                  (default): Linear(in_features=16, out_features=8960, bias=False)\n",
      "                )\n",
      "                (lora_embedding_A): ParameterDict()\n",
      "                (lora_embedding_B): ParameterDict()\n",
      "                (lora_magnitude_vector): ModuleDict()\n",
      "              )\n",
      "              (up_proj): lora.Linear4bit(\n",
      "                (base_layer): Linear4bit(in_features=1536, out_features=8960, bias=False)\n",
      "                (lora_dropout): ModuleDict(\n",
      "                  (default): Identity()\n",
      "                )\n",
      "                (lora_A): ModuleDict(\n",
      "                  (default): Linear(in_features=1536, out_features=16, bias=False)\n",
      "                )\n",
      "                (lora_B): ModuleDict(\n",
      "                  (default): Linear(in_features=16, out_features=8960, bias=False)\n",
      "                )\n",
      "                (lora_embedding_A): ParameterDict()\n",
      "                (lora_embedding_B): ParameterDict()\n",
      "                (lora_magnitude_vector): ModuleDict()\n",
      "              )\n",
      "              (down_proj): lora.Linear4bit(\n",
      "                (base_layer): Linear4bit(in_features=8960, out_features=1536, bias=False)\n",
      "                (lora_dropout): ModuleDict(\n",
      "                  (default): Identity()\n",
      "                )\n",
      "                (lora_A): ModuleDict(\n",
      "                  (default): Linear(in_features=8960, out_features=16, bias=False)\n",
      "                )\n",
      "                (lora_B): ModuleDict(\n",
      "                  (default): Linear(in_features=16, out_features=1536, bias=False)\n",
      "                )\n",
      "                (lora_embedding_A): ParameterDict()\n",
      "                (lora_embedding_B): ParameterDict()\n",
      "                (lora_magnitude_vector): ModuleDict()\n",
      "              )\n",
      "              (act_fn): SiLU()\n",
      "            )\n",
      "            (input_layernorm): Qwen2RMSNorm((1536,), eps=1e-06)\n",
      "            (post_attention_layernorm): Qwen2RMSNorm((1536,), eps=1e-06)\n",
      "          )\n",
      "        )\n",
      "        (norm): Qwen2RMSNorm((1536,), eps=1e-06)\n",
      "        (rotary_emb): LlamaRotaryEmbedding()\n",
      "      )\n",
      "      (lm_head): Linear(in_features=1536, out_features=151936, bias=False)\n",
      "    )\n",
      "  )\n",
      ")\n"
     ]
    }
   ],
   "source": [
    "# 因为 `model` 对象现在是由 Unsloth 创建的，它包含了所有必需的属性\n",
    "model = FastLanguageModel.get_peft_model(\n",
    "    model,\n",
    "    r=16,\n",
    "    target_modules=[\n",
    "      \"q_proj\",\n",
    "      \"k_proj\",\n",
    "      \"v_proj\",\n",
    "      \"o_proj\",\n",
    "      \"gate_proj\",\n",
    "      \"up_proj\",\n",
    "      \"down_proj\",\n",
    "    ],\n",
    "    lora_alpha=16,\n",
    "    lora_dropout=0,\n",
    "    bias=\"none\",\n",
    "    use_gradient_checkpointing=\"unsloth\",\n",
    "    random_state=1432,\n",
    "    use_rslora=False,\n",
    "    loftq_config=None,\n",
    ")\n",
    "# 检查模型结构，确认 LoRA 适配器已添加\n",
    "print(model)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 6. 配置 SFTTrainer\n",
    "\n",
    "`SFTTrainer` (Supervised Fine-tuning Trainer) 是一个专门用于指令微调的训练器。我们需要配置 `TrainingArguments` 来指定所有的训练参数，如批量大小、学习率、优化器等。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Detected kernel version 5.4.0, which is below the recommended minimum of 5.5.0; this can cause the process to hang. It is recommended to upgrade the kernel to the minimum version or higher.\n"
     ]
    }
   ],
   "source": [
    "from trl import SFTConfig, SFTTrainer\n",
    "trainer = SFTTrainer(\n",
    "    model = model,\n",
    "    tokenizer = tokenizer,\n",
    "    train_dataset = dataset,\n",
    "    dataset_text_field = \"text\",\n",
    "    max_seq_length = max_seq_length,\n",
    "    packing = False, # Can make training 5x faster for short sequences.\n",
    "    args = SFTConfig(\n",
    "        per_device_train_batch_size = 64,\n",
    "        gradient_accumulation_steps = 2,\n",
    "        warmup_steps = 5,\n",
    "        # num_train_epochs = 1, # Set this for 1 full training run.\n",
    "        max_steps = 60,\n",
    "        learning_rate = 2e-4,\n",
    "        logging_steps = 1,\n",
    "        optim = \"adamw_8bit\",\n",
    "        weight_decay = 0.01,\n",
    "        lr_scheduler_type = \"linear\",\n",
    "        seed = 1432,\n",
    "        output_dir = \"outputs\",\n",
    "        report_to = \"none\", # Use this for WandB etc\n",
    "    ),\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 7. 开始训练\n",
    "\n",
    "一切准备就绪后，调用 `trainer.train()` 即可开始微调过程。训练结束后，会返回包含训练统计信息（如训练损失）的对象。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "12.6\n"
     ]
    }
   ],
   "source": [
    "import torch; \n",
    "print(torch.version.cuda)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "==((====))==  Unsloth - 2x faster free finetuning | Num GPUs used = 1\n",
      "   \\\\   /|    Num examples = 20,171 | Num Epochs = 1 | Total steps = 60\n",
      "O^O/ \\_/ \\    Batch size per device = 64 | Gradient accumulation steps = 2\n",
      "\\        /    Data Parallel GPUs = 1 | Total batch size (64 x 2 x 1) = 128\n",
      " \"-____-\"     Trainable parameters = 18,464,768 of 1,795,552,768 (1.03% trained)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Unsloth: Will smartly offload gradients to save VRAM!\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "\n",
       "    <div>\n",
       "      \n",
       "      <progress value='60' max='60' style='width:300px; height:20px; vertical-align: middle;'></progress>\n",
       "      [60/60 1:16:32, Epoch 0/1]\n",
       "    </div>\n",
       "    <table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       " <tr style=\"text-align: left;\">\n",
       "      <th>Step</th>\n",
       "      <th>Training Loss</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <td>1</td>\n",
       "      <td>3.161000</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>2</td>\n",
       "      <td>3.169400</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>3</td>\n",
       "      <td>3.073900</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>4</td>\n",
       "      <td>3.141900</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>5</td>\n",
       "      <td>3.102200</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>6</td>\n",
       "      <td>2.951600</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>7</td>\n",
       "      <td>2.937500</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>8</td>\n",
       "      <td>2.910500</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>9</td>\n",
       "      <td>2.875000</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>10</td>\n",
       "      <td>2.773500</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>11</td>\n",
       "      <td>2.778600</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>12</td>\n",
       "      <td>2.687600</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>13</td>\n",
       "      <td>2.676400</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>14</td>\n",
       "      <td>2.605800</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>15</td>\n",
       "      <td>2.640800</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>16</td>\n",
       "      <td>2.590400</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>17</td>\n",
       "      <td>2.507600</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>18</td>\n",
       "      <td>2.497300</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>19</td>\n",
       "      <td>2.432600</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>20</td>\n",
       "      <td>2.435500</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>21</td>\n",
       "      <td>2.448700</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>22</td>\n",
       "      <td>2.395700</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>23</td>\n",
       "      <td>2.388100</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>24</td>\n",
       "      <td>2.362900</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>25</td>\n",
       "      <td>2.378800</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>26</td>\n",
       "      <td>2.338600</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>27</td>\n",
       "      <td>2.313300</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>28</td>\n",
       "      <td>2.333600</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>29</td>\n",
       "      <td>2.330100</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>30</td>\n",
       "      <td>2.325700</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>31</td>\n",
       "      <td>2.314800</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>32</td>\n",
       "      <td>2.374300</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>33</td>\n",
       "      <td>2.292400</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>34</td>\n",
       "      <td>2.297300</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>35</td>\n",
       "      <td>2.326600</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>36</td>\n",
       "      <td>2.284200</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>37</td>\n",
       "      <td>2.323500</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>38</td>\n",
       "      <td>2.320200</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>39</td>\n",
       "      <td>2.306700</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>40</td>\n",
       "      <td>2.242600</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>41</td>\n",
       "      <td>2.293700</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>42</td>\n",
       "      <td>2.286800</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>43</td>\n",
       "      <td>2.272900</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>44</td>\n",
       "      <td>2.249900</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>45</td>\n",
       "      <td>2.266600</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>46</td>\n",
       "      <td>2.255600</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>47</td>\n",
       "      <td>2.284200</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>48</td>\n",
       "      <td>2.256500</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>49</td>\n",
       "      <td>2.304500</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>50</td>\n",
       "      <td>2.276100</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>51</td>\n",
       "      <td>2.249800</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>52</td>\n",
       "      <td>2.304900</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>53</td>\n",
       "      <td>2.285700</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>54</td>\n",
       "      <td>2.237600</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>55</td>\n",
       "      <td>2.264800</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>56</td>\n",
       "      <td>2.295700</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>57</td>\n",
       "      <td>2.236800</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>58</td>\n",
       "      <td>2.239900</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>59</td>\n",
       "      <td>2.231900</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>60</td>\n",
       "      <td>2.243400</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table><p>"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "TrainOutput(global_step=60, training_loss=2.4664006431897483, metrics={'train_runtime': 4669.4568, 'train_samples_per_second': 1.645, 'train_steps_per_second': 0.013, 'total_flos': 7.163728311484416e+16, 'train_loss': 2.4664006431897483})\n"
     ]
    }
   ],
   "source": [
    "trainer_stats = trainer.train()\n",
    "\n",
    "# 打印训练统计信息\n",
    "print(trainer_stats)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 8. 保存微调后的模型（Lora）\n",
    "\n",
    "训练完成后，您可以再次进行推理，比较微调后的模型与原始模型的差异。如果对结果满意，可以使用 `model.save_pretrained(\"your_lora_adapter_path\")` 来保存训练好的 LoRA 适配器。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [],
   "source": [
    "model.save_pretrained(\"qwen-1.5b_lora_model\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "('qwen-1.5b_lora_model/tokenizer_config.json',\n",
       " 'qwen-1.5b_lora_model/special_tokens_map.json',\n",
       " 'qwen-1.5b_lora_model/chat_template.jinja',\n",
       " 'qwen-1.5b_lora_model/tokenizer.json')"
      ]
     },
     "execution_count": 17,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "tokenizer.save_pretrained(\"qwen-1.5b_lora_model\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 模型保存方式二选一（要么使用上面的分开保存，要么使用这里的合并 Lora 保存）\n",
    "# model.save_pretrained_merged(\"qwen-1.5b_lora_model\", tokenizer, save_method=\"merged_16bit\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 9. 测试训练后的生成结果"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [],
   "source": [
    "FastLanguageModel.for_inference(model) # Enable native 2x faster inference\n",
    "\n",
    "question=\"一个患有急性阑尾炎的病人已经发病5天，腹痛稍有减轻但仍然发热，在体检时发现右下腹有压痛的包块，此时应如何处理？\", # Question\n",
    "inputs = tokenizer([inference_prompt.format(question, \"\")], return_tensors=\"pt\").to(\"cuda\")\n",
    "\n",
    "outputs = model.generate(\n",
    "    input_ids=inputs.input_ids,\n",
    "    attention_mask=inputs.attention_mask,\n",
    "    max_new_tokens=1000,\n",
    "    use_cache=True,\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "<think>\n",
      "患者患有急性阑尾炎，已经发病5天了，这让他感到症状越来越明显。腹痛稍微减轻了，但还是有发热的情况，这些症状让我有点担心。在体检时，发现右下腹有压痛的包块，这让我有点紧张。\n",
      "\n",
      "首先，我需要确认这个包块的大小和形状，这样才能更好地进行诊断。如果包块比较大，可能需要进一步的检查。不过，如果包块很小，可能只是暂时性的，不需要太担心。\n",
      "\n",
      "接下来，我应该考虑是否需要手术。因为阑尾炎通常会导致大肠炎，而大肠炎需要及时处理，可能需要手术。但手术时间需要根据情况来决定。\n",
      "\n",
      "如果包块比较小，我需要考虑使用药物来处理。但使用药物可能需要医生的批准，而且药物的效果需要根据情况来评估。\n",
      "\n",
      "如果包块比较大，可能需要更仔细地观察症状，看看是否有其他症状，比如发热，这可能提示了感染或其他并发症。\n",
      "\n",
      "因此，我需要综合考虑患者的症状和包块的大小，决定最合适的处理方式。首先，确认包块的大小和形状，然后根据具体情况采取相应的措施。\n",
      "</think>\n",
      "在确认患者的症状后，首先需要明确包块的大小和形状。如果包块较大，可能需要进一步的检查，以确保包块的完整性。如果包块较小，可以考虑使用药物来处理，但药物的使用需要医生的批准。此外，如果包块较大，还需要观察是否有其他症状，如发热，以评估感染的可能性。建议在确认包块的完整性后，逐步采取措施，以确保患者能得到及时的治疗。\n",
      "\n"
     ]
    }
   ],
   "source": [
    "output = tokenizer.batch_decode(outputs, skip_special_tokens=True)\n",
    "print(output[0].split(\"### Response:\")[1])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [],
   "source": [
    "def generate_response(question: str, model, tokenizer, inference_prompt: str, max_new_tokens: int = 1024) -> str:\n",
    "    \"\"\"\n",
    "    使用指定的模型和分词器为给定的医学问题生成响应。\n",
    "\n",
    "    Args:\n",
    "        question (str): 需要模型回答的医学问题。\n",
    "        model: 已加载的 Unsloth/Hugging Face 模型。\n",
    "        tokenizer: 对应的分词器。\n",
    "        inference_prompt (str): 用于格式化输入的 f-string 模板。\n",
    "        max_new_tokens (int, optional): 生成响应的最大 token 数量。默认为 1024。\n",
    "\n",
    "    Returns:\n",
    "        str: 模型生成的响应文本，已去除 prompt 部分。\n",
    "    \"\"\"\n",
    "    # 1. 使用模板格式化输入\n",
    "    prompt = inference_prompt.format(\n",
    "        question, # 填充问题\n",
    "        \"\",       # 留空，让模型生成 CoT 和 Response\n",
    "    )\n",
    "\n",
    "    # 2. 将格式化后的 prompt 进行分词，并转移到 GPU\n",
    "    inputs = tokenizer([prompt], return_tensors=\"pt\").to(model.device)\n",
    "\n",
    "    # 3. 使用模型生成输出\n",
    "    # use_cache=True 用于加速解码过程\n",
    "    outputs = model.generate(\n",
    "        input_ids=inputs.input_ids,\n",
    "        attention_mask=inputs.attention_mask,\n",
    "        max_new_tokens=max_new_tokens,\n",
    "        use_cache=True,\n",
    "    )\n",
    "    \n",
    "    # 4. 将生成的 token 解码为文本\n",
    "    # skip_special_tokens=True 会移除像 EOS_TOKEN 这样的特殊标记\n",
    "    decoded_output = tokenizer.batch_decode(outputs, skip_special_tokens=True)[0]\n",
    "\n",
    "    # 5. 切分字符串，只返回 \"### Response:\" 之后的部分\n",
    "    # 使用 .split() 分割并获取响应内容，.strip() 用于去除可能存在的前后空白字符\n",
    "    response_part = decoded_output.split(\"### Response:\")\n",
    "    if len(response_part) > 1:\n",
    "        return response_part[1].strip()\n",
    "    else:\n",
    "        # 如果模型没有生成 \"### Response:\" 标记，则返回整个生成内容以供调试\n",
    "        return decoded_output"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 44,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "==================== 模型回答 ====================\n",
      "<think>\n",
      "嗯，这位60岁的男性患者有右侧胸疼，X线检查显示右侧肋膈角消失，这让我想到可能有肺结核的迹象。肺结核通常会导致胸腔积液，特别是右侧的。要弄清楚这种积液的具体性质，实验室检查应该是关键。\n",
      "\n",
      "首先，检查胸腔液体的酸碱度，能告诉我这个积液是酸性还是碱性。酸性或碱性可以帮助我们确定是什么类型的液体，比如乳酸水或者乳酸钠。如果怀疑是乳酸水，可以用乳酸水酶的检测方法来进一步确认。\n",
      "\n",
      "然后，看看是否有乳酸钠的存在，这在肺结核患者中非常重要，因为乳酸钠可以释放乳酸，帮助维持酸性环境。如果检测到乳酸钠，就能进一步验证积液的类型。\n",
      "\n",
      "此外，检测尿液中的乳酸含量也很重要，因为这能直接反映肺结核患者的乳酸含量，从而判断是否已经产生了乳酸水。\n",
      "\n",
      "最后，用血清中的血钙含量来判断血钙水平，因为血钙升高可能提示肺结核患者的血钙水平较高，也可能暗示了乳酸含量增加。\n",
      "\n",
      "综上所述，通过这些检查，我们能更准确地了解胸腔积液的具体性质和类型，从而做出更准确的诊断。\n",
      "</think>\n",
      "```\n",
      "在诊断肺结核伴右侧胸腔积液时，了解胸腔液体的酸碱度和乳酸含量是关键。以下是这些检查的步骤：\n",
      "\n",
      "1. **检查胸腔液体的酸碱度**：通过酸碱度计检测，可以确定胸腔液体是酸性还是碱性。酸性或碱性可以帮助我们初步判断是乳酸水或乳酸钠。\n",
      "\n",
      "2. **检测乳酸钠的存在**：如果怀疑是乳酸水，可以用乳酸水酶来检测，以确认乳酸钠的存在。\n",
      "\n",
      "3. **尿液中的乳酸检测**：尿液中的乳酸含量可以反映肺结核患者的乳酸含量，从而判断是否已开始产生乳酸水。\n",
      "\n",
      "4. **血清中的血钙检测**：血钙水平的变化可以提示血钙的水平，从而判断是否为肺结核患者的血钙水平较高。\n",
      "\n",
      "通过这些检查，我们能够更准确地了解胸腔积液的具体性质和类型，从而做出更准确的诊断。\n"
     ]
    }
   ],
   "source": [
    "my_question = \"对于一名60岁男性患者，出现右侧胸疼并在X线检查中显示右侧肋膈角消失，诊断为肺结核伴右侧胸腔积液，请问哪一项实验室检查对了解胸水的性质更有帮助？\"\n",
    "\n",
    "response = generate_response(my_question, model, tokenizer, inference_prompt)\n",
    "print(\"==================== 模型回答 ====================\")\n",
    "print(response)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "==================== 模型回答 ====================\n",
      "<think>\n",
      "嗯，这位28岁的程序员最近一周，每天工作到半夜，感觉头晕和脖子疼，还有时候恶心。这种感觉很奇怪，我得好好想想这是什么。\n",
      "\n",
      "首先，头晕和脖子疼，通常可能和头部的不适有关。有时候，像这样重大的疼痛和疲劳，可能是脑部的损伤。这让我想到脑损伤的可能性。\n",
      "\n",
      "脑损伤通常分为几种类型，比如脑膜炎、脑膜外伤、脑膜外伤后脑膜炎、外伤性脑膜炎、外伤性脑膜炎后脑膜炎等等。这些都可能导致头部的疼痛和疲劳。\n",
      "\n",
      "现在，我需要看看他最近工作的情况，是否和这些脑损伤有关。他工作到深夜，可能意味着他需要长时间的脑力活动，这可能与脑膜外伤或外伤性脑膜炎有关。\n",
      "\n",
      "再想想，他是不是经常感到恶心呢？恶心有时候是由于脑部的神经病变引起的。如果他的恶心是长期的，这可能进一步加剧了脑损伤。\n",
      "\n",
      "所以，综合起来，他可能有脑膜外伤。这种情况下，他的疼痛和恶心可能是由脑膜外伤引起的。\n",
      "\n",
      "当然，如果他最近的疼痛和恶心没有明显改善，或者在工作中出现了一些症状，可能需要更仔细地考虑，比如脑膜炎或者外伤性脑膜炎。\n",
      "\n",
      "不过，根据他目前的描述，最可能的解释还是脑膜外伤，尤其是在工作到深夜时，感觉会更加明显。\n",
      "</think>\n",
      "根据你描述的情况，这位28岁的程序员最近每天工作到半夜，感觉头晕、脖子疼，有时还恶心。这些症状可能与脑膜外伤有关。脑膜外伤通常会导致头部的疼痛和疲劳，尤其是在长期工作的情况下。这种疼痛和恶心是常见的脑损伤表现之一。\n",
      "\n",
      "考虑到这些症状，最有可能的解释是脑膜外伤。这种情况下，疼痛和恶心通常是因为脑膜外伤导致的神经病变，尤其是在工作时，这种损伤可能对头部的神经和肌肉造成严重的影响。\n",
      "\n",
      "因此，综合来看，你描述的症状最有可能与脑膜外伤有关。这种情况下，疼痛和恶心是脑损伤的常见表现，尤其是在长期的脑力劳动中。\n"
     ]
    }
   ],
   "source": [
    "my_question = \"男，28岁，程序员，最近一周每天工作到半夜，感觉头晕、脖子疼，有时候还恶心。\"\n",
    "\n",
    "response = generate_response(my_question, model, tokenizer, inference_prompt)\n",
    "print(\"==================== 模型回答 ====================\")\n",
    "print(response)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.13.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
